package com.atguigu.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.client.ProductFeignClient;
import com.atguigu.dao.ProductRepository;
import com.atguigu.entity.*;
import com.atguigu.search.*;
import com.atguigu.service.SearchService;
import lombok.SneakyThrows;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private ProductRepository productRepository;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    //上架的商品数据来源于数据库
    @Override
    public void onSale(Long skuId) {
        Product product = new Product();
        //1.商品的skuInfo
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
        if (skuInfo != null) {
            product.setId(skuInfo.getId());
            product.setProductName(skuInfo.getSkuName());
            product.setCreateTime(new Date());
            product.setPrice(skuInfo.getPrice().doubleValue());
            product.setDefaultImage(skuInfo.getSkuDefaultImg());
            //2.获取品牌信息
            BaseBrand brand = productFeignClient.getBrandById(skuInfo.getBrandId());
            if (brand != null) {
                product.setBrandId(brand.getId());
                product.setBrandName(brand.getBrandName());
                product.setBrandLogoUrl(brand.getBrandLogoUrl());
            }
            //3.查询分类信息
            BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
            if (categoryView != null) {
                product.setCategory1Id(categoryView.getCategory1Id());
                product.setCategory1Name(categoryView.getCategory1Name());
                product.setCategory2Id(categoryView.getCategory2Id());
                product.setCategory2Name(categoryView.getCategory2Name());
                product.setCategory3Id(categoryView.getCategory3Id());
                product.setCategory3Name(categoryView.getCategory3Name());
            }
        }
        //4.根据skuId查询平台属性信息
        List<PlatformPropertyKey> platformPropertyList = productFeignClient.getPlatformPropertyBySkuId(skuId);
        if (!CollectionUtils.isEmpty(platformPropertyList)) {
            List<SearchPlatformProperty> searchPlatformPropertyList = platformPropertyList.stream().map(platformProperty -> {
                SearchPlatformProperty searchPlatformProperty = new SearchPlatformProperty();
                //平台属性id
                searchPlatformProperty.setPropertyKeyId(platformProperty.getId());
                //平台属性名称
                searchPlatformProperty.setPropertyKey(platformProperty.getPropertyKey());
                //平台属性值
                List<PlatformPropertyValue> propertyValueList = platformProperty.getPropertyValueList();
                //一个确定sku的平台属性名称只对应一个平台属性值
                String propertyValue = propertyValueList.get(0).getPropertyValue();
                searchPlatformProperty.setPropertyValue(propertyValue);
                return searchPlatformProperty;
            }).collect(Collectors.toList());
            product.setPlatformProperty(searchPlatformPropertyList);
        }
        //5.最终应该把这些数据存储在es中
        productRepository.save(product);
    }

    @Override
    public void offSale(Long skuId) {
        productRepository.deleteById(skuId);
    }

    @Override
    public void incrHotScore(Long skuId) {
        //定义一个缓存的key
        String hotKey = "sku:hotscore";
        //用zset的结构加1操作
        double count = redisTemplate.opsForZSet().incrementScore(hotKey, skuId, 1);
        //加到一定是会对es里面的字段进行修改
        if (count % 6 == 0) {
            //先从es中查询出该商品 然后修改保存
            Optional<Product> optional = productRepository.findById(skuId);
            Product esProduct = optional.get();
            esProduct.setHotScore(Math.round(count));
            productRepository.save(esProduct);
        }
    }

    @SneakyThrows
    @Override
    public SearchResponseVo searchProduct(SearchParam searchParam) {
        //1.生成动态dsl查询语句
        SearchRequest searchRequest = this.buildQueryDsl(searchParam);
        //2.通过该语句实现查询
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //3.将查询到的结果封装到SearchResponseVo里面
        SearchResponseVo searchResponseVo = this.parseSearchResult(searchResponse);
        //4.其他返回参数的赋值
        searchResponseVo.setPageNo(searchParam.getPageNo());
        searchResponseVo.setPageSize(searchParam.getPageSize());
        //5.设置总页数
        boolean addPage = searchResponseVo.getTotal() % searchParam.getPageSize() == 0;
        long totalPages = 0;
        if (addPage) {
            totalPages = searchResponseVo.getTotal() / searchParam.getPageSize();
        } else {
            totalPages = searchResponseVo.getTotal() / searchParam.getPageSize() + 1;
        }
        searchResponseVo.setTotalPages(totalPages);
        return searchResponseVo;
    }

    //3.将查询到的结果封装到SearchResponseVo里面
    private SearchResponseVo parseSearchResult(SearchResponse searchResponse) {
        SearchResponseVo searchResponseVo = new SearchResponseVo();
        //1.拿到商品的基本信息
        SearchHits firstHits = searchResponse.getHits();
        SearchHit[] secondHits = firstHits.getHits();
        ArrayList<Product> productList = new ArrayList<>();
        if (secondHits != null && secondHits.length > 0) {
            for (SearchHit secondHit : secondHits) {
                //把json字符串转换为对象
                Product product = JSONObject.parseObject(secondHit.getSourceAsString(), Product.class);
                HighlightField highLightField = secondHit.getHighlightFields().get("productName");
                if (highLightField != null) {
                    //拿取高亮信息
                    Text highLightProductName = highLightField.getFragments()[0];
                    product.setProductName(highLightProductName.toString());
                }
                productList.add(product);
            }
        }
        searchResponseVo.setProductList(productList);
        searchResponseVo.setTotal(firstHits.totalHits);
        //2.拿到品牌的信息
        ParsedLongTerms brandIdAgg = searchResponse.getAggregations().get("brandIdAgg");
        List<SearchBrandVo> searchBrandVoList = brandIdAgg.getBuckets().stream().map(bucket -> {
            SearchBrandVo searchBrandVo = new SearchBrandVo();
            //a.拿取品牌的id
            String brandId = bucket.getKeyAsString();
            searchBrandVo.setBrandId(Long.parseLong(brandId));
            //b.拿取品牌的name
            ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brandNameAgg");
            String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
            searchBrandVo.setBrandName(brandName);
            //c.拿取品牌的logo_url
            ParsedStringTerms brandLogoUrlAgg = bucket.getAggregations().get("brandLogoUrlAgg");
            String brandLogoUrl = brandLogoUrlAgg.getBuckets().get(0).getKeyAsString();
            searchBrandVo.setBrandLogoUrl(brandLogoUrl);
            return searchBrandVo;
        }).collect(Collectors.toList());
        searchResponseVo.setBrandVoList(searchBrandVoList);
        //3.拿到平台属性信息
        ParsedNested platformPropertyAgg = searchResponse.getAggregations().get("platformPropertyAgg");
        //拿嵌套agg的子agg
        ParsedLongTerms propertyKeyIdAgg = platformPropertyAgg.getAggregations().get("propertyKeyIdAgg");
        List<? extends Terms.Bucket> buckets = propertyKeyIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(buckets)) {
            List<SearchPlatformPropertyVo> platformPropertyVoList = buckets.stream().map(bucket -> {
                SearchPlatformPropertyVo platformPropertyVo = new SearchPlatformPropertyVo();
                //a.平台属性Id
                Number propertyKeyId = bucket.getKeyAsNumber();
                platformPropertyVo.setPropertyKeyId(propertyKeyId.longValue());
                //b.平台属性名称
                ParsedStringTerms propertyKeyAgg = bucket.getAggregations().get("propertyKeyAgg");
                String propertyKey = propertyKeyAgg.getBuckets().get(0).getKeyAsString();
                platformPropertyVo.setPropertyKey(propertyKey);
                //c.平台属性值的集合
                ParsedStringTerms propertyValueAgg = bucket.getAggregations().get("propertyValueAgg");
                List<? extends Terms.Bucket> propertyValueBuckets = propertyValueAgg.getBuckets();
                //拿到平台属性值的集合
                List<String> propertyValueList = propertyValueBuckets.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
                platformPropertyVo.setPropertyValueList(propertyValueList);
                return platformPropertyVo;
            }).collect(Collectors.toList());
            searchResponseVo.setPlatformPropertyList(platformPropertyVoList);
        }
        return searchResponseVo;
    }


    //1.生成动态dsl查询语句
    private SearchRequest buildQueryDsl(SearchParam searchParam) {
        //1.构造bool字段
        BoolQueryBuilder firstBool = QueryBuilders.boolQuery();
        //2.构造分类id过滤器
        if (!StringUtils.isEmpty(searchParam.getCategory1Id())) {
            //构造一级分类id过滤器
            TermQueryBuilder category1Id = QueryBuilders.termQuery("category1Id", searchParam.getCategory1Id());
            firstBool.filter(category1Id);
        }
        if (!StringUtils.isEmpty(searchParam.getCategory2Id())) {
            //构造二级分类id过滤器
            TermQueryBuilder category2Id = QueryBuilders.termQuery("category2Id", searchParam.getCategory2Id());
            firstBool.filter(category2Id);
        }
        if (!StringUtils.isEmpty(searchParam.getCategory3Id())) {
            //构造三级分类id过滤器
            TermQueryBuilder category3Id = QueryBuilders.termQuery("category3Id", searchParam.getCategory3Id());
            firstBool.filter(category3Id);
        }
        //3.构造品牌过滤条件 参数格式 brandName=1:苹果
        String brandName = searchParam.getBrandName();
        if (!StringUtils.isEmpty(brandName)) {
            String[] brandParam = brandName.split(":");
            if (brandParam.length == 2) {
                firstBool.filter(QueryBuilders.termQuery("brandId", brandParam[0]));
            }
        }
        //4.构造平台属性过滤条件  参数格式 &props=4:苹果A14:CPU型号&props=5:5.0英寸以下:屏幕尺寸
        String[] props = searchParam.getProps();
        if (props != null && props.length > 0) {
            for (String prop : props) {
                //props=4:苹果A14:CPU型号
                String[] platformParams = prop.split(":");
                if (platformParams.length == 3) {
                    //a.构建bool
                    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                    //b.构建子bool
                    BoolQueryBuilder childBoolQuery = QueryBuilders.boolQuery();
                    //c.子bool里面构造must
                    childBoolQuery.must(QueryBuilders.termQuery("platformProperty.propertyKeyId", platformParams[0]));
                    childBoolQuery.must(QueryBuilders.termQuery("platformProperty.propertyValue", platformParams[1]));
                    boolQuery.must(QueryBuilders.nestedQuery("platformProperty", childBoolQuery, ScoreMode.None));
                    firstBool.filter(boolQuery);
                }
            }
        }
        //5.构造商品名称搜索关键字查询 参数格式 keyword=三星
        String keyword = searchParam.getKeyword();
        if (!StringUtils.isEmpty(keyword)) {
            MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("productName", keyword).operator(Operator.AND);
            firstBool.must(matchQuery);
        }
        //6.构造query
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(firstBool);
        //7.设置分页 起始页: (pageNo-1)*pageSize
        int from = (searchParam.getPageNo() - 1) * searchParam.getPageSize();
        sourceBuilder.from(from);
        sourceBuilder.size(searchParam.getPageSize());
        /**
         * 8.设置排序  参数格式 order=2:desc
         * 参数 1---综合(hotScore) 2----价格(price)
         */
        String order = searchParam.getOrder();
        if (!StringUtils.isEmpty(order)) {
            String[] orderParams = order.split(":");
            String fileName = null;
            if (orderParams.length == 2) {
                switch (orderParams[0]) {
                    case "1":
                        fileName = "hotScore";
                        break;
                    case "2":
                        fileName = "price";
                        break;
                }
                //把排序字符转换为对象
                sourceBuilder.sort(fileName, "asc".equals(orderParams[1]) ? SortOrder.ASC : SortOrder.DESC);
            }
        } else {
            //按照默认方式排序 综合(hotScore)降序
            sourceBuilder.sort("hotScore", SortOrder.DESC);
        }
        //9.设置高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("productName");
        highlightBuilder.preTags("<span style=color:red>");
        highlightBuilder.postTags("</span>");
        sourceBuilder.highlighter(highlightBuilder);
        //10.设置品牌聚合
        TermsAggregationBuilder brandIdAggBuilder = AggregationBuilders.terms("brandIdAgg").field("brandId")
                .subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName"))
                .subAggregation(AggregationBuilders.terms("brandLogoUrlAgg").field("brandLogoUrl"));
        sourceBuilder.aggregation(brandIdAggBuilder);

        //11.设置平台属性聚合
        sourceBuilder.aggregation(AggregationBuilders.nested("platformPropertyAgg", "platformProperty")
                .subAggregation(AggregationBuilders.terms("propertyKeyIdAgg").field("platformProperty.propertyKeyId")
                        .subAggregation(AggregationBuilders.terms("propertyKeyAgg").field("platformProperty.propertyKey"))
                        .subAggregation(AggregationBuilders.terms("propertyValueAgg").field("platformProperty.propertyValue"))
                ));
        //12.设置需要返回的字段
        sourceBuilder.fetchSource(new String[]{"id", "defaultImage", "productName", "price"}, null);
        //13.设置查询哪个index和type
        SearchRequest request = new SearchRequest("product");
        request.types("info");
        request.source(sourceBuilder);
        System.out.println("拼接好的dsl语句" + sourceBuilder.toString());
        return request;

    }
}
